/**
 * Copyright 2010, Lars J. Nilsson <http://www.larsan.net> 
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package net.larsan.dconf.jbc;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jboss.cache.Cache;
import org.jboss.cache.DefaultCacheFactory;

import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.name.Named;

public class CacheProvider implements Provider<Cache<String, String>> {

	private static final String CLASSPATH = "classpath:";

	@Inject
	@Named("jbcConfig")
	private String path;
	
	@Inject
	@Named("jbcBroadcastAddress")
	private String broadcastAddress; 
	
	@Inject(optional=true)
	@Named("jbcBindAddress")
	private String bindAddress; 
	
	private final Log log = LogFactory.getLog(getClass());

	@Override
	public Cache<String, String> get() {
		log.info("Cache provider creating cache with config path: " + path);
		return new DefaultCacheFactory<String, String>().createCache(getConfigStream(), true);
	}

	
	// --- PRIVATE METHODS --- //
	
	private InputStream getConfigStream() {
		String tmp = readAsString();
		tmp = tmp.replace("${broadcastAddress}", broadcastAddress);
		log.debug("Replacing config broadcast adress with '" + broadcastAddress + "'");
		if(bindAddress != null) {
			log.debug("Replacing config bind adress with '" + bindAddress + "'");
			tmp = tmp.replace("${bindAddressAttribute}", "bind_addr=\"" + bindAddress + "\"");
		} else {
			tmp = tmp.replace("${bindAddressAttribute}", "");	
		}
		try {
			return new ByteArrayInputStream(tmp.getBytes("UTF-8"));
		} catch (UnsupportedEncodingException e) {
			throw new IllegalStateException("No UTF-8 in system?!", e);
		}
	}

	private String readAsString() {
		BufferedReader r = null;
		StringBuilder b = new StringBuilder();
		try {
			int len = -1;
			char[] buff = new char[256];
			InputStream in = findConfigStream();
			r = new BufferedReader(new InputStreamReader(in));
			while((len = r.read(buff)) != -1) {
				b.append(buff, 0, len);
			}
		} catch(FileNotFoundException e) {
			throw new IllegalArgumentException("Resource '" + path + "' not found in classpath", e);
		} catch(MalformedURLException e) {
			throw new IllegalArgumentException("Path '" + path + "' is not a legal URL", e);
		} catch(IOException e) {
			throw new IllegalStateException("Failed to read config from '" + path + "'", e);
 		} finally {
			safeClose(r);
		}
		return b.toString();
	}

	private InputStream findConfigStream() throws MalformedURLException, IOException {
		if(path.startsWith(CLASSPATH)) {
			log.debug("Reading config from class path: " + path);
			String tmp = path.substring(CLASSPATH.length());
			InputStream in = getClass().getClassLoader().getResourceAsStream(tmp);
			if(in == null) throw new FileNotFoundException("No such class path resource: " + tmp);
			return in;
		} else {
			log.debug("Reading config from URL: " + path);
			return new URL(path).openStream();
		}
	}

	private void safeClose(BufferedReader r) {
		if(r == null) return; // SANITY CHECK
		try {
			r.close();
		} catch(IOException e) { }
	}
}
